--函数！

--用法：local HX = require "packages/huixiangkill/hxfs"
--如HX.DrawCardFromPile(player.room, player, baguan2022.name, 2)

local hxfs = {}


--选择一种类型的牌，从牌堆中依次摸任意张
---@param room Room @ 房间
---@param player ServerPlayer @ 摸牌的角色
---@param Skill string @ 技能名
---@param num integer @ 要摸的数量
function hxfs.DrawCardFromPile(room, player, Skill, num)
    local choice = room:askToChoice(player,{
        choices = {"基本牌", "锦囊牌", "装备牌"},
        skill_name = Skill,
        prompt = "请选择摸的牌的类型",
    })
    local cardType = ""
    if choice == "基本牌" then
        cardType = "basic"
    elseif choice == "锦囊牌" then
        cardType = "trick"
    elseif choice == "装备牌" then
        cardType = "equip"
    else
        return -- 如果选择无效，直接返回
    end
    for i = 1, num do
      local cards = room:getCardsFromPileByRule(".|.|.|.|.|" .. cardType)
      if #cards > 0 then
        room:obtainCard(player, cards[1], false, fk.ReasonDraw, player, Skill)
      end
    end
end


---通过仅改变武将改变武将图像
---@param room Room @ 房间
---@param player ServerPlayer 改变图像的角色
---@param old string   需要改变的武将名
---@param new string   改变后的武将名
function hxfs.ChangeGeneral(room, player, old, new)
  if player.deputyGeneral == old or player.general == old then
    if player.deputyGeneral == old then
      player.deputyGeneral = new
      room:broadcastProperty(player, "deputyGeneral")
    elseif player.general == old then
      player.general = new
      room:broadcastProperty(player, "general")
    end
  else
    player.general = new
    room:broadcastProperty(player, "general")
  end
end

---设置一名角色所有技能的已发动次数（发送战报）
---@param room Room @ 房间
---@param player ServerPlayer @ 角色
---@param times integer @ 要设置的次数
function hxfs.SetSkillsTimes(room, player, times)
  local skills = table.simpleClone(Fk.generals[player.general]:getSkillNameList(true))
  if player.deputyGeneral ~= "" then
    table.insertTableIfNeed(skills, Fk.generals[player.deputyGeneral]:getSkillNameList(true))
  end
  skills = table.filter(skills, function(s) return player:hasSkill(s) end)
  if #skills > 0 then
    for _, s in ipairs(skills) do
      player:setSkillUseHistory(s, times, Player.HistoryPhase)
      player:setSkillUseHistory(s, times, Player.HistoryTurn)
      player:setSkillUseHistory(s, times, Player.HistoryRound)
      player:setSkillUseHistory(s, times, Player.HistoryGame)
    end
    room:sendLog{
      type = "%from 所有技能的发动次数已被设置为 "..times.." 次！",
      from = player.id,
      --arg = s,
    }
  end
end

---设置一名角色的一个技能的已发动次数（发送战报）
---@param room Room @ 房间
---@param player ServerPlayer @ 角色
---@param skill string @ 技能名
---@param times integer @ 要设置的次数
function hxfs.SetTheSkillTimes(room, player, skill, times)
  player:setSkillUseHistory(skill, times, Player.HistoryPhase)
  player:setSkillUseHistory(skill, times, Player.HistoryTurn)
  player:setSkillUseHistory(skill, times, Player.HistoryRound)
  player:setSkillUseHistory(skill, times, Player.HistoryGame)
  room:sendLog{
    type = "%from 的【"..Fk:translate(skill).."】的发动次数已被设置为 "..times.." 次。",
    from = player.id,
    --arg = s,
  }
end

---令一名角色将手牌数摸至对应值
---@param room Room @ 房间
---@param player ServerPlayer @ 角色
---@param num integer @ 值
---@param Skill string @ 技能名
---@param discard? boolean @是否包含弃置
function hxfs.SetPlayerHandNum(room, player, num, Skill, discard)
  local n = player:getHandcardNum()
  if n < num then
    player:drawCards(num-n, Skill)
  end
  if discard then
    if n > num then
      room:askToDiscard(player, {
        min_num = n-num,
        max_num = n-num,
        include_equip = false,
        skill_name = Skill,
        cancelable = false,
      })
    end
  end
end


---令一名角色从牌堆中获得一张指定牌名的牌（牌堆没有会从弃牌堆中获得）
---@param room Room @ 房间
---@param player ServerPlayer @ 角色
---@param name string @ 牌名
---@param Skill string @ 技能名
---@param moveMark? table|string @ 移动后自动赋予标记，格式：{标记名(支持-inarea后缀，移出值代表区域后清除), 值}
function hxfs.GetCardFromPile(room, player, name, Skill, moveMark)
  local card1 = table.filter(room.draw_pile, function(id)
    return Fk:getCardById(id).trueName == name
  end)
  if #card1 > 0 then
    local card = Fk:getCardById(table.random(card1))
    room:obtainCard(player, card, false, fk.ReasonPrey, player, Skill, moveMark)
  else
    local card1_2 = table.filter(room.discard_pile, function(id)
      return Fk:getCardById(id).trueName == name
    end)
    if #card1_2 > 0 then
      local card = Fk:getCardById(table.random(card1_2))
      room:obtainCard(player, card, false, fk.ReasonPrey, player, Skill, moveMark)
    end
  end
end


---令一名角色从牌堆中获得一张指定点数的牌（牌堆没有会从弃牌堆中获得）
---@param room Room @ 房间
---@param player ServerPlayer @ 角色
---@param number string @ 点数
---@param Skill string @ 技能名
function hxfs.GetNumberCardFromPile(room, player, number, Skill)
  local card1 = table.filter(room.draw_pile, function(id)
    return Fk:getCardById(id).number == number
  end)
  if #card1 > 0 then
    local card = Fk:getCardById(table.random(card1))
    room:obtainCard(player, card, false, fk.ReasonPrey, player, Skill)
  else
    local card1_2 = table.filter(room.discard_pile, function(id)
      return Fk:getCardById(id).number == number
    end)
    if #card1_2 > 0 then
      local card = Fk:getCardById(table.random(card1_2))
      room:obtainCard(player, card, false, fk.ReasonPrey, player, Skill)
    end
  end
end


---令一名角色从牌堆中获得一张指定花色的牌（牌堆没有会从弃牌堆中获得）
---@param room Room @ 房间
---@param player ServerPlayer @ 角色
---@param suit integer @ 花色
---@param Skill string @ 技能名
function hxfs.GetSuitCardFromPile(room, player, suit, Skill)
  local card1 = table.filter(room.draw_pile, function(id)
    return Fk:getCardById(id).suit == suit
  end)
  if #card1 > 0 then
    local card = Fk:getCardById(table.random(card1))
    room:obtainCard(player, card, false, fk.ReasonPrey, player, Skill)
  else
    local card1_2 = table.filter(room.discard_pile, function(id)
      return Fk:getCardById(id).suit == suit
    end)
    if #card1_2 > 0 then
      local card = Fk:getCardById(table.random(card1_2))
      room:obtainCard(player, card, false, fk.ReasonPrey, player, Skill)
    end
  end
end


---令一名角色从牌堆中获得一张指定类型的牌（牌堆没有会从弃牌堆中获得）
---@param room Room @ 房间
---@param player ServerPlayer @ 角色
---@param type string @ 类型
---@param Skill string @ 技能名
function hxfs.GetTypeCardFromPile(room, player, type, Skill)
  local card1 = table.filter(room.draw_pile, function(id)
    return Fk:getCardById(id).type == type
  end)
  if #card1 > 0 then
    local card = Fk:getCardById(table.random(card1))
    room:obtainCard(player, card, false, fk.ReasonPrey, player, Skill)
  else
    local card1_2 = table.filter(room.discard_pile, function(id)
      return Fk:getCardById(id).type == type
    end)
    if #card1_2 > 0 then
      local card = Fk:getCardById(table.random(card1_2))
      room:obtainCard(player, card, false, fk.ReasonPrey, player, Skill)
    end
  end
end


---实现租号技能的主要逻辑，技能描述如下：
---每轮开始时，你可以展示至多x张武将牌，若如此做，你根据下面的关键词依次选择这些武将牌："..
---"<br/>①“摸牌”；"..
---"<br/>②“视为”；"..
---"<br/>③“伤害”。"..
---"<br/>若被选武将的某个技能的描述中包含对应关键词，<br/>则你本轮获得此技能；若被选武将无此类技能，你减1点体力上限。"..
---"（x为你的体力上限且至少为3，至多为100）"
---@param room Room @ 房间
---@param player ServerPlayer @ 角色
---@param pattern1 string @ 租号词条1
---@param pattern2 string @ 租号词条2
---@param pattern3 string @ 租号词条3
---@param Skill string @ 技能名
---@param MIN? integer @ x的最小值，至少为3
---@param skill? boolean @是否不失去技能
---@param maxhp? boolean @是否不减体力上限
---@return string[] @ 获得的技能
function hxfs.ZuHaoSelect(room, player, pattern1,pattern2,pattern3, Skill, MIN, skill, maxhp)
  local choices = {}
  local number
  --接下来的11行，控制展示武将的最大值，最小值的控制则直接写在了可选武将初始化部分
  local max = player.maxHp
  if MIN then
    if max < MIN then
      max = MIN
    end
  end
  for i = 1, max do
    if i <= 100 then  --最多100个，太多了会把服务器搞崩（100上限以上的你们输了）
      table.insert(choices, tostring(i))
    end
  end
  if player.id <= 0 then return {} end  --排除人机
  if #choices > 2 then
    --定义类型解决黄色波浪线
    number = tonumber(room:askToChoice(player, {choices = choices, skill_name = Skill ,cancelable = false, prompt = "租号：请选择至多"..#choices.."张武将牌展示", })) ---@type integer
  else  --吃到保底了就不选了
    number = #choices
  end
  --232到247行，抄来的筛选武将的方法，筛进来了同名武将，正合租号技能的要求
  local existingGenerals = {}
  table.forEach(room.players, function(p)
    table.insertTable(existingGenerals, {p.general, p.deputyGeneral})
  end)
  local allgenerals, same_generals = {}, {}
  for _, general_name in ipairs(room.general_pile) do   --（超绝for循环要开始了）
    same_generals = Fk:getSameGenerals(general_name)
    table.insert(same_generals, general_name)
    same_generals = table.filter(same_generals, function (g_name)
      local general = Fk.generals[g_name]
      return true
    end)
    if #same_generals > 0 then
      table.insert(allgenerals, table.random(same_generals))
    end
  end
  if #allgenerals == 0 then return {} end  --判空这一块./（但是真的会没武将可用吗。。）
  local choices_draw, skills_draw = {}, {}   --词条1的所有武将及技能
  local choices_damage, skills_damage = {}, {}   --词条2的所有武将及技能
  local choices_view, skills_view = {}, {}   --词条3的所有武将及技能
  local total_choices = {}   --3个词条的所有武将
  local total_skills = {}   --3个词条的所有技能
  for _, general_name in ipairs(allgenerals) do   --从所有武将中筛选所有可选武将
    local general = Fk.generals[general_name]
    local total = table.filter(general:getSkillNameList(true), function(s)
      return (string.find(Fk:getDescription(s, "zh_CN"), pattern1) ~= nil) or
      (string.find(Fk:getDescription(s, "zh_CN"), pattern2) ~= nil) or
      (string.find(Fk:getDescription(s, "zh_CN"), pattern3) ~= nil)
    end)
    if #total > 0 then
      table.insert(total_choices, general_name)
      for _, s in ipairs(total) do
        table.insertIfNeed(total_skills, s)   --那么问题来了，这里的IfNeed加不加有区别吗。。
      end
    end
  end
  local generals = {}  --可选武将的……初始化？
  local has_generals = table.random(total_choices, 3)  --保底3个词条武将
  local total_randomchoices = {}  --随机的所有武将
  local has_randomgenerals = {}  --3个保底武将之外的随机武将
  for _, general_name in ipairs(has_generals) do  --将3个词条武将加入到可选武将
    table.insert(generals, general_name)
  end
  if number > 3 then  --4上限福利，随机武将的筛选
    for _, general_name in ipairs(allgenerals) do   --从所有武将中排除保底武将
      if not table.contains(has_generals, general_name) then
        table.insert(total_randomchoices, general_name)
      end
    end
    has_randomgenerals = table.random(total_randomchoices, number-3)  --真·随机武将
    for _, general_name in ipairs(has_randomgenerals) do   --将随机武将加入到可选武将
      table.insert(generals, general_name)
    end
  end
  for _, general_name in ipairs(generals) do  --依次把可选武将及他们各自的技能分类进表里
    local general = Fk.generals[general_name]
    local to_draw = table.filter(general:getSkillNameList(true), function(s) --词条1武将的技能
      return (string.find(Fk:getDescription(s, "zh_CN"), pattern1) ~= nil)
    end)
    local to_damage = table.filter(general:getSkillNameList(true), function(s) --词条2武将的技能
      return (string.find(Fk:getDescription(s, "zh_CN"), pattern2) ~= nil)
    end)
    local to_view = table.filter(general:getSkillNameList(true), function(s) --词条3武将的技能
      return (string.find(Fk:getDescription(s, "zh_CN"), pattern3) ~= nil)
    end)
    if #to_draw > 0 then  --词条1武将及其技能的录入
      table.insert(choices_draw, general_name)
      for _, s in ipairs(to_draw) do
        table.insertIfNeed(skills_draw, s)
      end
    end
    if #to_damage > 0 then  --词条2武将及其技能的录入
      table.insert(choices_damage, general_name)
      for _, s in ipairs(to_damage) do
        table.insertIfNeed(skills_damage, s)
      end
    end
    if #to_view > 0 then  --词条3武将及其技能的录入
      table.insert(choices_view, general_name)
      for _, s in ipairs(to_view) do
        table.insertIfNeed(skills_view, s)
      end
    end
  end
  local getskills = {} -- 将要获得的技能
  local togeneral = {} -- 武租哥没有原画，这是他随机变身的武将列表
  local generals_tran = {}  -- 武将的翻译表，用来把可选武将显示在战报里
  for _, g in ipairs(generals) do  --录入武将翻译
    table.insert(generals_tran, Fk:translate(g))
  end
  local allpattern = {}  --所有词条，当然也是为了可以显示在战报里
  table.insert(allpattern, pattern1)  --词条1录入到所有词条
  table.insert(allpattern, pattern2)  --词条2录入到所有词条
  table.insert(allpattern, pattern3)  --词条3录入到所有词条
  room:sendLog({type = "#zuhao_log", from = player.id, arg = table.concat(allpattern, "、"), arg2 = table.concat(generals_tran, "、"),})  --发送战报
  local draw = room:askToCustomDialog( player, {  --询问选择词条1的正确武将
    skill_name = Skill,  --技能名
    qml_path = "packages/huixiangkill/qml/HX__ChooseGeneralsAndChoiceBox.qml",  --调用的框的路径，这里用的是改的经典的选武将的框
    extra_data = {  --词条2和词条3同理
      generals, --可选武将
      {"OK"},  --选项按钮
      "租号：请选择任意个武将,若被选武将的某个技能的描述中包含“"..pattern1.."”,<br/>则你本轮获得此技能;若被选武将无此类技能，你减1点体力上限",  --提示信息
      {"Cancel"},  --取消按钮？反正如果只写{}的话会只有确定按钮
      1, --最小选择数
      number,  --最大选择数
    },
  })
  -- if draw == "" then return end  --烧条直接return是不行的，优化成正常的进行下一个选择才对，下面的同理
  local draw_true = {}  --选对的词条1武将
  local truegeneral_1 = {}  --选对的词条1武将的翻译
  local falsegeneral_1 = {}  --选错的词条1武将的翻译
  if draw and draw ~= "" then
    local reply = draw
    draw = reply.cards
    for _, general_name in ipairs(draw) do
      if table.contains(choices_draw, general_name) then
        table.insert(draw_true, general_name)   --录入选对的词条1武将
        table.insert(truegeneral_1, Fk:translate(general_name))  --录入选对的词条1武将的翻译
      else
        table.insert(falsegeneral_1, Fk:translate(general_name))  --录入选错的词条1武将的翻译
      end
    end
    generals = table.filter(generals, function(g) return not table.contains(draw, g) end)  --将词条1的武将筛出去
    for _, general_name in ipairs(draw_true) do  --将选对的词条1武将的技能加入到获得技能表
      local general = Fk.generals[general_name]
      for _, getskill in ipairs(general:getSkillNameList(true)) do
        if table.contains(skills_draw, getskill) then
          table.insertIfNeed(getskills, getskill)  --录入词条1中可获得的技能
          table.insertIfNeed(togeneral, general_name)  --录入词条1中可变身的武将
        end
      end
    end
  end
  local draw_false = math.max(0, #draw - #draw_true)  --词条1选错的武将数
  local damage = room:askToCustomDialog( player, {  --询问选择词条2的正确武将
    skill_name = Skill,
    qml_path = "packages/huixiangkill/qml/HX__ChooseGeneralsAndChoiceBox.qml",
    extra_data = {
      generals,
      {"OK"},
      "租号：请选择任意个武将,若被选武将的某个技能的描述中包含“"..pattern2.."”,<br/>则你本轮获得此技能;若被选武将无此类技能，你减1点体力上限",
      {"Cancel"},
      1,
      number,
    },
  })
  local damage_true = {}  --选对的词条2武将
  local truegeneral_2 = {}  --选对的词条2武将的翻译
  local falsegeneral_2 = {}  --选错的词条2武将的翻译
  if damage and damage ~= "" then
    local reply2 = damage
    damage = reply2.cards
    for _, general_name in ipairs(damage) do  
      if table.contains(choices_damage, general_name) then
        table.insert(damage_true, general_name)  --录入选对的词条2武将
        table.insert(truegeneral_2, Fk:translate(general_name))  --录入选对的词条2武将的翻译
      else
        table.insert(falsegeneral_2, Fk:translate(general_name))  --录入选错的词条2武将的翻译
      end
    end
    generals = table.filter(generals, function(g) return not table.contains(damage, g) end)  --将词条2的武将筛出去
    for _, general_name in ipairs(damage_true) do
      local general = Fk.generals[general_name]
      for _, getskill in ipairs(general:getSkillNameList(true)) do
        if table.contains(skills_damage, getskill) then
          table.insertIfNeed(getskills, getskill)  --录入词条2中可获得的技能
          table.insertIfNeed(togeneral, general_name)  --录入词条2中可变身的武将
        end
      end
    end
  end
  local damage_false = math.max(0, #damage - #damage_true)  --词条2选错的武将数
  local view = room:askToCustomDialog( player, {  --询问选择词条3的正确武将
    skill_name = Skill,
    qml_path = "packages/huixiangkill/qml/HX__ChooseGeneralsAndChoiceBox.qml",
    extra_data = {
      generals,
      {"OK"},
      "租号：请选择任意个武将,若被选武将的某个技能的描述中包含“"..pattern3.."”,<br/>则你本轮获得此技能;若被选武将无此类技能，你减1点体力上限",
      {"Cancel"},
      1,
      number,
    },
  })
  local view_true = {}  --选对的词条3武将
  local truegeneral_3 = {}  --选对的词条3武将的翻译
  local falsegeneral_3 = {}  --选错的词条3武将的翻译
  if view and view ~= "" then
    local reply3 = view
    view = reply3.cards
    for _, general_name in ipairs(view) do
      if table.contains(choices_view, general_name) then
        table.insert(view_true, general_name)  --录入选对的词条3武将
        table.insert(truegeneral_3, Fk:translate(general_name))  --录入选对的词条3武将的翻译
      else
        table.insert(falsegeneral_3, Fk:translate(general_name))  --录入选错的词条3武将的翻译
      end
    end
    for _, general_name in ipairs(view_true) do
      local general = Fk.generals[general_name]
      for _, getskill in ipairs(general:getSkillNameList(true)) do
        if table.contains(skills_view, getskill) then
          table.insertIfNeed(getskills, getskill)  --录入词条3中可获得的技能
          table.insertIfNeed(togeneral, general_name)  --录入词条3中可变身的武将
        end
      end
    end
  end
  local view_false = math.max(0, #view - #view_true)  --词条3选错的武将数
  local maxhplost = draw_false + damage_false + view_false  --选错的武将总数，用于体力上限减少及战报显示
  if #getskills > 0 then
    room:sendLog({type = "#zuhao_true_log", from = player.id, --发送选对的武将
    arg = table.concat(truegeneral_1, "、") ,
    arg2 = table.concat(truegeneral_2, "、") ,
    arg3 = table.concat(truegeneral_3, "、") })
    if player.general == "hxqunyou__wuzuge" or player.deputyGeneral == "hxqunyou__wuzuge" or player:hasSkill("hx__zuhao", true) then  --武租哥的变身
      local tochange = table.random(togeneral, 2)  --控制随机性
      player.general = tochange[1]
      if #tochange > 1 then
        player.deputyGeneral = tochange[2]
        room:broadcastProperty(player, "deputyGeneral")
      end
      room:broadcastProperty(player, "general")
    end
    local mark = player:getTableMark("@[:]zuhao_skills-round") or {}  --租号技能显示
    for _, g in ipairs(getskills) do
      table.insertIfNeed(mark, g)  --录入技能
    end
    room:setPlayerMark(player, "@[:]zuhao_skills-round", mark)  --设置可点开查看的标记，就是技能显示
    room:handleAddLoseSkills(player, table.concat(getskills, "|"))  --获得技能
    if not skill then
      local round_event = room.logic:getCurrentEvent():findParent(GameEvent.Round)  --只获得一轮
      if round_event ~= nil then
        round_event:addCleaner(function()
          room:handleAddLoseSkills(player, "-"..table.concat(getskills, "|-"))  --移去技能
        end)
      end
    end
  end
  if maxhplost > 0 then
    room:sendLog({type = "#zuhao_false_log", from = player.id, --发送选错的武将
    arg = table.concat(falsegeneral_1, "、") ,
    arg2 = table.concat(falsegeneral_2, "、") ,
    arg3 = table.concat(falsegeneral_3, "、") })
    if not maxhp then
      room:sendLog({type = "#zuhao_falsenum_log", from = player.id, --发送要减少的体力上限
      arg = maxhplost })
      for i = 1, maxhplost do
        room:changeMaxHp(player, -1)  --减上限
      end
    end
  end
  return getskills
end



-- 有X%的概率返回1 = if HasRandom(X) then return 1 end
---@param X? integer @ x的最小值，默认为1
function hxfs.HasRandom(X)
  local randomNumber = math.random(1, 100)
  return randomNumber <= X
end


---下面两个函数为 表情包柴郡 的技能的主要逻辑

---分别选择要移去的表情包和要给予的表情包，执行完后，返回的toremove是玩家要移除的两个表情包构成的表
---@param room Room 房间
---@param player ServerPlayer 玩家
---@param mark table 玩家当前的表情包
---@param skill_name string 技能名
function hxfs.ChuanDi(room, player, mark, skill_name)
  local toremove = {}
  local result = room:askToCustomDialog( player, {
    skill_name = skill_name,
    qml_path = "packages/utility/qml/ChooseGeneralsAndChoiceBox.qml",
    extra_data = {
      player:getTableMark("@&hx__moyin_emoji"),
      {"OK"},
      "#hx__chuandi-remove",
      {},
      1,
      1,
    },
  })
  if result and result ~= "" then
    local reply = result
    table.insert(toremove, reply.cards[1])
  else
    table.insert(toremove, table.random(mark))
  end
  table.removeOne(mark, toremove[1])
  if #mark > 1 then
    result = room:askToCustomDialog( player, {
      skill_name = skill_name,
      qml_path = "packages/utility/qml/ChooseGeneralsAndChoiceBox.qml",
      extra_data = {
        mark,
        {"OK"},
        "#hx__chuandi-give",
        {},
        1,
        1,
      },
    })
    if result and result ~= "" then
      local reply = result
      table.insert(toremove, 2, reply.cards[1])
    else
      table.insert(toremove, 2, table.random(mark))
    end
  else
    table.insert(toremove, 2, table.random(mark))
  end
  table.removeOne(mark, toremove[2])
  return toremove
end


---on_use部分，更新标记、切换目标武将图、玩家摸两张牌
---@param room Room 房间
---@param player ServerPlayer 玩家
---@param mark table 玩家当前的表情包
---@param to ServerPlayer 接收表情包的玩家
---@param toremove table 要移除的表情包
---@param skill_name string 技能名
function hxfs.AfterChuanDi(room, player, mark, to, toremove, skill_name)
  room:setPlayerMark(player, "@&hx__moyin_emoji", mark)
  room:addTableMark(to, "@&hx__moyin_emoji", toremove[2])
  to.general = toremove[2]
  to.deputyGeneral = ""
  room:broadcastProperty(to, "general")
  room:broadcastProperty(to, "deputyGeneral")
  player:drawCards(2, skill_name)
end


---上面两个函数为 表情包柴郡 的技能的主要逻辑


--————————————忆者模式——————————————————



--- 获得当前房间中的忆者，其实就是找内奸，同时也要在对应的tag里
---@param room Room @ 房间
---@return ServerPlayer[]? @ 忆者列表
function hxfs.getyizhe(room)
  local allyizhe = room:getTag("hx__simulator_allyizhe")
  local players = {}
  for _, p in ipairs(room.players) do
    if p.role == "renegade" and table.contains(allyizhe, p) then
      table.insert(players, p)
    end
  end

  return players
end

--- 判断一名角色是否是忆者
---@param room Room @ 房间
---@param player ServerPlayer @ 角色
---@return boolean @ 是否是忆者
function hxfs.Isyizhe(room, player)
  local allyizhe = room:getTag("hx__simulator_allyizhe")
  if player.role == "renegade" and table.contains(allyizhe, player) then
    return true
  end
  return false
end


--————————————忆者模式——————————————————


--[[
10.1幻矩思路：默认分配3千金豆，第10局重置，胜利的豆子改为地主豆
存档结构：
num ，金豆数量
over，有没有恢复金豆，存在该参数就是恢复过
dizhu，地主豆数量
gametimes，游戏场数，每次达到10则令 num = 3000
，当然我改了一下原思路
初始化：
num = 3000
over = 1 --直接当作恢复过金豆吧，相关判断在pkg\gamemodes\hx_1v2_jindou.lua
dizhu = 0
gametimes = 0 --结算金豆的时候加1，然后发现为10就设置为0，num再设置为3000，结算相关在pkg\gamemodes\rule_skills\hx__1v2_rule.lua，当然还有将星招募

那么先前的数据怎么处理呢？用伟大的if else吧。
当前的结构是：
num，金豆数量
over = 1,或者干脆没有
主要还有一个可能是有已有数据但是没有恢复的，那这一类只好冷处理了，发现他高于原金豆的时候就把over设置为1吧，只好还是在金豆恢复里设置一次了。
那么其他属性就如下了，主要还是防止有不存在的参数
首先gametimes就还是0，num改为3000，原来的值赋给dizhu
直接判断有没有地主豆来确定有没有进行转换又不太好，还是得再加一个参数，用switch来表示吧


唉，还是初来乍到好
--]]

-- 初始化并返回最新存档数据
---@param player ServerPlayer @ 玩家
---@param mode string @ 实则只有"hx_1v2_jindou"，只给金豆用吧
---@return table @ 数据
function hxfs.GetModeData(player, mode)
  if player.id < 0 then return {  num = 0, over = 1, switch = 1, dizhu = 0, gametimes = 0} end
  local globalData = player:getGlobalSaveState(mode)
  local data = 3000 --3千金豆
  if next(globalData) == nil then
    globalData.num = data
    globalData.over = 1
    globalData.switch = 1
    globalData.dizhu = 1000 --赠送1000地主豆吧
    globalData.gametimes = 0
    player:saveGlobalState(mode, globalData)
    player.room:sendLog{ type = "#hx_1v2_initMoney", from = player.id, toast = true }
  else
    if not globalData.switch then
      globalData.switch = 1
      globalData.gametimes = 0
      globalData.dizhu = math.max(math.abs(globalData.num), 1000)
      globalData.num = data
      player:saveGlobalState(mode, globalData)
    end
  end
  return globalData
end

--改变玩家的金豆及地主豆
---@param room Room @ 房间
---@param mode string @ 依旧是"hx_1v2_jindou"，果然抄来之物不如自写啊
---@param player ServerPlayer @ 玩家
---@param num integer @ 改变量
---@param win? boolean @ 是否胜利,true则按绝对值增加地主豆且不改变金豆，默认当然是不结算
---@param isdizhu? boolean @ 是否改为改变地主豆
function hxfs.ChangePlayerMoney(room, mode, player, num, win, isdizhu)
  if player.id <= 0 or num == 0 then return false end
  if room:getSettings("enableFreeAssign") then
    room:sendLog{ type = "#hx_1v2_asktoclose", toast = true }
    return false
  end
  if table.find(room.players, function(p) return p.id <= 0 end) and not room:isGameMode("hxstar_shop") then
    num = num / 2
    room:sendLog{ type = "#hx_1v2_tipsforai", toast = true }
  end

  local data = hxfs.GetModeData(player, mode)  --调用该函数后则无需判空了，玩家必定有对应数据
  win = win or false
  isdizhu = isdizhu or false
  local data_num = data.num
  if isdizhu then
    data.dizhu = data.dizhu + num
  else
    data.num = data_num + num
  end
  if win then
    data.num = data_num  --胜者不扣豆
    data.dizhu = data.dizhu + math.abs(num)
  end
  -- @!!hx_1v2_jindou 是地主豆，hx_1v2_jindou 是金豆，@[hxjindou] 是用于显示金豆的qml标记
  room:setPlayerMark(player, "@!!"..mode, data.dizhu)

  room:setPlayerMark(player, mode, data.num)
  room:setPlayerMark(player, "@[hxjindou]"..mode, 1)

  player:saveGlobalState(mode, data)
  room:sendLog{ type = "#hx_1v2_changeMoneyOver", from = player.id, toast = true }
end

-- 更新玩家标记并返回金豆数量
---
-- 金豆就用标记显示吧，地主豆用图片标记
---@param room Room @ 房间
---@param mode string @ "hx_1v2_jindou"，唉，金豆机制
---@param player ServerPlayer @ 玩家
---@param dizhu? boolean @ 是否返回地主豆数量，默认返回金豆数量
---@return integer @ 返回数量
function hxfs.GetPlayerMoney(room, mode, player, dizhu)
  if player.id <= 0 then return 0 end
  local data = hxfs.GetModeData(player, mode)
  room:setPlayerMark(player, "@!!"..mode, data.dizhu)

  room:setPlayerMark(player, mode, data.num)
  room:setPlayerMark(player, "@[hxjindou]"..mode, 1)
  dizhu = dizhu or false
  if dizhu then
    return data.dizhu
  else
    return data.num
  end
end

--- 增加玩家场数，若达到10则重置金豆
---@param room Room @ 房间
---@param mode string @ "hx_1v2_jindou"，唉，金豆机制
---@param player ServerPlayer @ 玩家
function hxfs.SavePlayerTimes(room, mode, player)
  if player.id <= 0 then return false end
  if room:getSettings("enableFreeAssign") then
    room:sendLog{ type = "#hx_1v2_asktoclose-game", toast = true }
    return false
  end
  local gamedata = hxfs.GetModeData(player, mode)
  gamedata.gametimes = gamedata.gametimes + 1
  if gamedata.gametimes >= 10 then
    gamedata.num = 3000
    gamedata.gametimes = 0
  end
  player:saveGlobalState(mode, gamedata)
end




--- 从已经开启的拓展包中，随机选出若干名武将。
---
--- 同名武将不会重复选取，武将数量也会返回能找到的武将
---@param num integer @ 要选出的武将数量
---@param generalPool? string[] @ 选择的范围，默认是已经启用的所有武将的武将名
---@param except? string[] @ 特别要排除掉的武将名列表，默认是空表
---@param filter? fun(g: General): boolean? @ 可选参数，若这个函数返回true的话这个武将被排除在外
---@return General[] @ 随机选出的武将列表
function hxfs.getGenerals(num, generalPool, except, filter)
  if filter then
    assert(type(filter) == "function")
  end

  generalPool = generalPool or table.map(Fk:getAllGenerals(), Util.NameMapper)
  except = except or {}
  except = table.simpleClone(except)
  for _, g in ipairs(Fk.packages["test_p_0"].generals) do
    table.insert(except, g.name)
  end

  local availableGenerals = {}
  for _, gn in pairs(generalPool) do
    local general = Fk.generals[gn]
    if not table.contains(except, general.name) and not (filter and filter(general)) then
      if (not general.hidden and not general.total_hidden) and
        #table.filter(availableGenerals, function(g)
        return g.trueName == general.trueName
      end) == 0 then
        table.insert(availableGenerals, general)
      end
    end
  end
  return table.random(availableGenerals, num)
end


---从牌堆（或弃牌堆）内随机抽任意张牌
---@param room Room @ 房间
---@param pattern string @ 查找规则
---@param num? number @ 查找数量
---@param fromPile? "drawPile" | "discardPile" | "allPiles" @ 查找的来源区域，默认从牌堆内寻找
---@param x? number @ 查找牌堆的前x张，默认为所有
---@return integer[] @ id列表 可能空
function hxfs.getCardsFromPileByRule(room, pattern, num, fromPile, x)
  num = num or 1
  local pileToSearch = room.draw_pile
  if fromPile == "discardPile" then
    pileToSearch = room.discard_pile
  elseif fromPile == "allPiles" then
    pileToSearch = table.simpleClone(room.draw_pile)
    table.insertTable(pileToSearch, room.discard_pile)
  end

  if #pileToSearch == 0 then
    return {}
  end

  x = x or #pileToSearch
  pileToSearch = table.slice(pileToSearch, 1, x + 1)

  local matchedIds = {}
  for _, id in ipairs(pileToSearch) do
    if Fk:getCardById(id):matchPattern(pattern) then
      table.insert(matchedIds, id)
    end
  end

  if #matchedIds == 0 then
    return {}
  end

  local cardPack = {}

  local loopTimes = math.min(num, #matchedIds)
  local i
  for _ = 1, loopTimes do
    i = math.random(1, #matchedIds)
    table.insert(cardPack, matchedIds[i])
    table.remove(matchedIds, i)
  end

  return cardPack
end

-----打八张相关

--打八张之摸牌，其实就是从牌堆顶摸牌，没牌就不摸
---@param room Room @ 房间
---@param player ServerPlayer @ 摸牌的玩家
---@param num integer @ 摸牌数
---@param skillName? string @ 技能名
---@return integer[] @ 摸到的牌
function hxfs.AWEC_drawCards(room, player, num, skillName)
  if num < 1 or player.dead then
    return {}
  end
  local drawData = DrawData:new{
    who = player,
    num = num,
    skillName = skillName,
    fromPlace = "top",
  }
  if room.logic:trigger(fk.BeforeDrawCard, player, drawData) then
    return {}
  end
  num = drawData.num
  player = drawData.who
  local topCards = table.slice(room.draw_pile, 1, num + 1)
  if #topCards > 0 then
    room:moveCards({
      ids = topCards,
      to = player,
      toArea = Card.PlayerHand,
      moveReason = fk.ReasonDraw,
      proposer = player,
      skillName = skillName,
    })
  end
  hxfs.AWEC_Over(room)

  return { table.unpack(topCards) }
end

---打八张之补牌，即摸至num张牌
---@param room Room @ 房间
---@param player ServerPlayer @ 角色
---@param num integer @ 值
---@param skillName string @ 技能名
function hxfs.AWEC_SetHand(room, player, num, skillName)
  local n = player:getHandcardNum()
  if n < num then
    hxfs.AWEC_drawCards(room, player, num-n, skillName)
  end
end

--打八张之攻牌，返回两个值，第一个是攻的牌，第二个是可跟的点数
---@param room Room @ 房间
---@param skill_name string @ 技能名
---@param lordid integer @ 防守方玩家id
---@param lastid? integer @ 进攻方id，默认为防守方的上家
---@return integer[] @ 攻的牌
---@return integer[] @ 可跟点数
function hxfs.AWEC_Attack(room, skill_name, lordid, lastid)
  local lord = room:getPlayerById(lordid)
  lastid = lastid or lord:getLastAlive().id
  local last = room:getPlayerById(lastid)
  local tocards = {}
  local nojoker = table.filter(last:getCardIds("h"), function (id)
    return Fk:getCardById(id).name ~= "hx__joker" and Fk:getCardById(id).name ~= "hx__joker2"
  end)
  local joker = table.filter(last:getCardIds("h"), function (id) return Fk:getCardById(id).name == "hx__joker" end)
  local joker2 = table.filter(last:getCardIds("h"), function (id) return Fk:getCardById(id).name == "hx__joker2" end)
  if #nojoker == 0 and #joker + #joker2 > 0 then
    local getcards = table.random(room.discard_pile, #joker * 5 + #joker2 * 3)
    if #getcards > 0 then
      last:showCards(last:getCardIds("h"))
      room:sendLog{ type = "%from 的王炸在了手中！需从弃牌堆中摸共计%arg张牌！", from = last.id, arg = #joker * 5 + #joker2 * 3, toast = true}
      room:delay(1000)
      room:moveCards{
        ids = getcards,
        to = last,
        toArea = Card.PlayerHand,
        moveReason = fk.ReasonJustMove,
        proposer = last,
        skillName = skill_name,
      }
    else
      local nextid = last:getNextAlive().id
      room:setBanner("hx__AWEC_mode-Lord", nextid)
      room:sendLog{ type = "%from 的王炸在手中！由于弃牌堆中无牌可摸，其直接出局！下一个防守方是 %to", from = last.id, to = {nextid}, toast = true}
      room:killPlayer{who = last }
      hxfs.AWEC_Over(room)
      lord = room:getPlayerById(nextid)
      last = room:getPlayerById(lord:getLastAlive().id)
    end
  end
  nojoker = table.filter(last:getCardIds("h"), function (id) return Fk:getCardById(id).name ~= "hx__joker" and Fk:getCardById(id).name ~= "hx__joker2" end)
  local success, dat = room:askToUseActiveSkill(last, {
    skill_name = "hx__AWEC_give",
    prompt = "打八张：请选择任意张点数均相同的牌攻给 %src :"..lord.id,
    cancelable = false,
  })
  if success and dat then
    tocards = dat.cards
  else
    tocards = table.random(nojoker, 1)
  end
  local gender = Fk.generals[last.general].gender
  local card = Fk:getCardById(tocards[1], true)
  if gender == General.Female then
    room:broadcastPlaySound("./packages/huixiangkill/audio/card/female/"..card.name)
  else
    room:broadcastPlaySound("./packages/huixiangkill/audio/card/male/"..card.name)
  end
  room:moveCards{
    ids = tocards,
    from = last,
    toArea = Card.Processing,
    moveReason = fk.ReasonJustMove,
    skillName = skill_name,
    proposer = last,
    moveVisible = true,
  }
  local numbers = {}
  for _, c in ipairs(tocards) do
    local C = Fk:getCardById(c, true)
    table.insertIfNeed(numbers, C.number)
  end
  table.sort(numbers)
  room:setBanner("hx__AWEC_mode-CanNum", numbers)
  room:sendLog{
    type = "%from 攻给 %to 的牌是：<br/> %card，跟牌的点数可以是：%arg",
    from = last.id,
    to = {lord.id},
    card = tocards,
    arg = table.concat(numbers, "、"),
    toast = true,
  }
  room:setBanner("hx__AWEC_mode-NextCard", tocards)
  hxfs.AWEC_Over(room)
  return tocards, numbers
end

--打八张之跟牌，注意跟牌依赖于room:getBanner("hx__AWEC_mode-CanNum")
---@param room Room @ 房间
---@param skill_name string @ 技能名
---@param others ServerPlayer[] 跟牌的玩家们
---@param lordid integer @ 防守方玩家id
function hxfs.AWEC_With(room, skill_name, others, lordid)
  local nextcards = room:getBanner("hx__AWEC_mode-NextCard") or {}
  local lord = room:getPlayerById(lordid)
  for _, p in ipairs(others) do
    local max = lord:getHandcardNum()
    if max <= #nextcards or p.dead or lord.dead then break end
    local number = room:getBanner("hx__AWEC_mode-CanNum") or {}
    local numberstr = table.concat(number, "、")
    local success2, dat2 = room:askToUseActiveSkill(p, {
      skill_name = "hx__AWEC_with",
      prompt = "打八张：你可以跟牌 %src ，当前可跟的点数有 %arg :"..lordid.."::"..numberstr,
      cancelable = false,
    })
    if success2 and dat2 then
      if #dat2.cards > 0 then
        for _, c in ipairs(dat2.cards) do
          table.insert(nextcards, c)
        end
        local gender = Fk.generals[p.general].gender
        local card = Fk:getCardById(dat2.cards[1], true)
        if gender == General.Female then
          room:broadcastPlaySound("./packages/huixiangkill/audio/card/female/"..card.name)
        else
          room:broadcastPlaySound("./packages/huixiangkill/audio/card/male/"..card.name)
        end
        room:moveCards{
          ids = dat2.cards,
          from = p,
          toArea = Card.Processing,
          moveReason = fk.ReasonJustMove,
          skillName = skill_name,
          proposer = p,
          moveVisible = true,
        }
        room:sendLog{
          type = "%from 选择了跟牌 %to，跟的牌是：<br/> %card",
          from = p.id,
          to = {lordid},
          card = dat2.cards,
          toast = true,
        }
      else
        room:sendLog{
          type = "%from 选择了不跟牌 %to",
          from = p.id,
          to = {lordid},
          toast = true,
        }
      end
    end
    room:setBanner("hx__AWEC_mode-NextCard", nextcards)
    hxfs.AWEC_Over(room)
  end
  room:setBanner("hx__AWEC_mode-NextCard", nextcards)
  hxfs.AWEC_Over(room)
end

--打八张之判断一名玩家的胜利，什么时候能把排名结算做出来
---@param room Room @ 房间
---@param player ServerPlayer @ 玩家
function hxfs.AWEC_judgeWin(room, player)
  if player:getHandcardNum() == 0 and #room.draw_pile == 0 then
    local rank = room:getBanner("hx__AWEC_mode-rank") or 1
    local type = "%from 获得了第%arg名！"
    if rank > 2 then
      type = "%from 获得第%arg名"
    end
    if rank < #room.players then
      room:setPlayerProperty(player, "role", "renegade")
    end
    room:sendLog{ type = type, from = player.id, arg = rank, toast = true }
    room:setBanner("hx__AWEC_mode-rank", rank + 1)
    room:delay(2000)
    room:killPlayer{ who = player }
  end
end

--打八张之游戏结束判断，跟牌压牌出牌摸牌结束都判断一次
---@param room Room @ 房间
function hxfs.AWEC_Over(room)
  for _, p in ipairs(room.alive_players) do
    hxfs.AWEC_judgeWin(room, p)
  end
  if #room.alive_players <= 1 then
    room:gameOver("renegade")
  end
end

--打八张之游戏流程
---@param room Room @ 房间
---@param player ServerPlayer @ 玩家
---@param lordid integer @ 防守方玩家id
---@param players ServerPlayer[] @ 排列好的玩家们
---@param skill_name string @ 技能名
function hxfs.AWEC_AWEC(room, player, lordid, players, skill_name)
  room:broadcastPlaySound("./packages/huixiangkill/audio/skill/hx__AWEC1")
  while true do
    --上家攻主
    lordid = room:getBanner("hx__AWEC_mode-Lord") or player.id
    local lord = room:getPlayerById(lordid)
    local lastid = lord:getLastAlive().id
    local sortplayers = {}
    local last = room:getPlayerById(lastid)
    table.insert(sortplayers, last)
    table.insert(sortplayers, lord)
    for _, p2 in ipairs(players) do
      table.insertIfNeed(sortplayers, p2)
    end
    local others = table.filter(sortplayers, function (p) return p.id ~= lord.id end)
    local tocards, numbers = {} , {}
    local tocards1, numbers1 = hxfs.AWEC_Attack(room, skill_name, lordid)
    table.insertTable(tocards, tocards1)
    table.insertTable(numbers, numbers1)
    --压牌循环
    while true do
      --其他玩家跟牌
      hxfs.AWEC_With(room, skill_name, others, lordid)
      local nextcards = room:getBanner("hx__AWEC_mode-NextCard") or {}
      if #nextcards == 0 then break end
      local result = room:askToArrangeCards(lord, {
        skill_name = skill_name,
        card_map = {
          "攻牌", nextcards,
          "你的牌", lord:getCardIds("h"),
        },
        prompt = "打八张：请压牌，让每一列的牌花色相同且第二行的牌的点数更大（主无视花色，大小王无视点数和花色）"..
        "<br/>点数规则：2＜3＜4＜5＜6＜7＜8＜9＜10＜J＜Q＜K＜A＜主牌",
        free_arrange = true,
        box_size = 11,
        max_limit = {#nextcards, #lord:getCardIds("h")},
        min_limit = {#nextcards, #lord:getCardIds("h")},
        poxi_type = "hx__AWEC_at",
        default_choice = nil,
      })
      if result then
        local boom = false  --是否炸牌
        result[2] = table.slice(result[2], 1 , #result[1] + 1)
        for _, c in ipairs(result[2]) do
          table.insert(tocards, c)
          local C = Fk:getCardById(c, true)
          if C.number > 0 then
            table.insertIfNeed(numbers, C.number)
          end
          if C.name == "hx__joker" or C.name == "hx__joker2" then
            boom = true
          end
        end
        table.sort(numbers)
        room:setBanner("hx__AWEC_mode-CanNum", numbers)
        room:setBanner("hx__AWEC_mode-NextCard", {})
        local gender = Fk.generals[lord.general].gender
        local card = Fk:getCardById(result[2][1], true)
        if card then
          if gender == General.Female then
            room:broadcastPlaySound("./packages/huixiangkill/audio/card/female/"..card.name)
          else
            room:broadcastPlaySound("./packages/huixiangkill/audio/card/male/"..card.name)
          end
          room:moveCards{
            ids = result[2],
            from = lord,
            toArea = Card.Processing,
            moveReason = fk.ReasonJustMove,
            skillName = skill_name,
            proposer = lord,
            moveVisible = true,
          }
          room:sendLog{
            type = "%from 压牌使用的牌是：<br/> %card",
            from = lord.id,
            card = result[2],
            toast = true,
          }
          if boom then
            room:sendLog{ type = "%from 使用了王，炸掉了所有牌！", from = lord.id, toast = true }
            break
          else
            room:sendLog{
              type = "%from 压牌成功，跟牌的点数可以是：<br/> %arg",
              from = lord.id,
              arg = table.concat(numbers, "、"),
              toast = true,
            }
            if lord:getHandcardNum() <= 0 then
              break
            else
              hxfs.AWEC_With(room, skill_name, others, lordid)
            end
          end
        else
          room:setBanner("hx__AWEC_mode-getallcard", 1)
          room:sendLog{ type = "%from 选择了收牌", from = lord.id, toast = true }
          room:moveCardTo(room.processing_area, Player.Hand, lord, fk.ReasonJustMove, skill_name, nil, true, lord)
          break
        end
      else
        room:sendLog{ type = "压牌出现未知错误，%from收牌！", from = lord.id, toast = true }
        room:moveCardTo(room.processing_area, Player.Hand, lord, fk.ReasonJustMove, skill_name, nil, true, lord)
      end
    end
    --获取log
    local function getPlayerStr(pid, color)
      local p = room:getPlayerById(pid)
      local str = '<font color="%s"><b>%s</b></font>'
      if p.general == "anjiang" and (p.deputyGeneral == "anjiang"
        or not p.deputyGeneral) then
        local ret = Fk:translate("seat#" .. p.seat)
        return string.format(str, color, ret)
      end
      local ret = p.general
      ret = Fk:translate(ret)
      if p.deputyGeneral and p.deputyGeneral ~= "" then
        ret = ret .. "/" .. Fk:translate(p.deputyGeneral)
      end
      for _, p2 in ipairs(Fk:currentRoom().players) do
        if p2 ~= p and p2.general == p.general and p2.deputyGeneral == p.deputyGeneral then
          ret = ret .. ("[%d]"):format(p.seat)
          break
        end
      end
      ret = string.format(str, color, ret)
      return ret
    end
    hxfs.AWEC_Over(room) --先结算一下
    local getallcard = room:getBanner("hx__AWEC_mode-getallcard") or 0
    local nextid = lord:getNextAlive().id  --下一个被攻者
    local next = room:getPlayerById(nextid)  --下一个被攻者本人
    if lord.dead then  --上一个被攻者压牌中胜利，下一个被攻者的下家变为被攻者
      nextid = next:getNextAlive().id
    end
    local from = getPlayerStr(nextid, "#0C8F0C")  --下一个被攻者log
    --发送log
    if getallcard == 1 then --如果收牌
      room:setBanner("hx__AWEC_mode-getallcard", 0)
      local nextnext = lord:getNextAlive().id
      nextid = next:getNextAlive().id
      from = getPlayerStr(nextid, "#0C8F0C")
      room:sendLog{
        type = " %from 选择了收牌，轮到 %to 攻其的下家 %arg",
        from = lord.id,
        to = {nextnext},
        toast = true,
        arg = from,
      }
    else
      if lord.dead then
        room:sendLog{
          type = "%from 在压牌中胜利，直接轮到 %to 攻其的下家 %arg",
          from = lord.id,
          to = {next.id},
          toast = true,
          arg = from,
        }
      else
        room:sendLog{
          type = "%from 攻 %to 结束，轮到 %to 攻其的下家 %arg",
          from = lastid,
          to = {lord.id},
          toast = true,
          arg = from,
        }
      end
    end
    --压牌结束的善后处理
    local allcard = room.processing_area
    room:moveCardTo(allcard, Card.DiscardPile, nil, fk.ReasonJustMove, skill_name, nil, true, lord)
    room:setBanner("hx__AWEC_mode-Lord", nextid)
    room:delay(1000)
    --补牌
    for _, p2 in ipairs(sortplayers) do
      hxfs.AWEC_SetHand(room, p2, 8, skill_name)
    end
  end
end


---@class askToTwoChoicesParams
---@field extra_data askToTwoChoicesParamsextra_data @ 额外数据
---@field skill_name string @ 烧条时显示的技能名

---@class askToTwoChoicesParamsextra_data
---@field prompt string @ 框的提示信息
---@field cancelable boolean @ 是否可以取消
---@field choices string[] @ 选择一的选项
---@field all_choices string[] 选择一的所有选项
---@field min_num number @ 选择一的最少选择数
---@field max_num number @ 选择一的最多选择数
---@field choices2 string[] @ 选择二的选项
---@field all_choices2 string[] @ 选择二的所有选项
---@field min_num2 number @ 选择二的最少选择数
---@field max_num2 number @ 选择二的最多选择数

--TODO:
-- 在同一次询问中进行两种选择，当前返回的值是json索引，有时间再尝试改成值吧
---
-- 见packages\huixiangkill\pkg\huixiangyi\skills\xishui.lua
---@param player ServerPlayer @ 询问的角色
---@param params askToTwoChoicesParams @ 各种变量
---@return any @ result.r1和result.r2，都是索引表
function hxfs.askToTwoChoices(player, params)
  local command = "CustomDialog"
  local req = Request:new(player, command)
  req.focus_text = params.skill_name
  req:setData(player, {
    path = "packages/huixiangkill/qml/TwoCheckBox.qml",
    data = params.extra_data,
  })
  local result = req:getResult(player)
  return result
end

---@class HXaskToJointChoiceParams
---@field players ServerPlayer[] @ 被询问的玩家
---@field choices string[] @ 可选选项列表
---@field skill_name string @ 技能名
---@field prompt string @ 提示信息
---@field send_log? boolean @ 是否发Log，默认否

--- 同时询问多名玩家从众多选项中选择一个（要求所有玩家选项相同，不同的要自行构造request）
--- 
--- 返回的值依旧是索引表，反而更好用了说是
---@param player ServerPlayer @ 发起者
---@param params HXaskToJointChoiceParams @ 各种变量
---@return table<Player, string> @ 返回键值表，键为Player、值为索引
function hxfs.askToJointChoice(player, params)
  local skillName = params.skill_name or "AskForChoice"
  local prompt = params.prompt or "AskForChoice"
  local players, choices = params.players, params.choices
  local sendLog = params.send_log or false

  local req = Request:new(players, "CustomDialog")
  req.focus_text = skillName
  req.receive_decode = false
  local data = {
    choices,
    choices,  --如果all_choices和choices不一样应该自行构造request
    skillName,
    prompt,
  }
  for _, p in ipairs(players) do
    req:setData(p, {
      path = "packages/huixiangkill/qml/ChoiceBox.qml",
      data = data,
    })
    req:setDefaultReply(p, math.random(#choices))  --默认项为随机选项
  end
  req:ask()
  if sendLog then
    for _, p in ipairs(players) do
      p.room:sendLog{
        type = "#Choice",
        from = p.id,
        arg = choices[req:getResult(p)],
        toast = true,
      }
    end
  end
  local ret = {}
  for _, p in ipairs(players) do
    ret[p] = req:getResult(p)
  end
  return ret
end

---@class HXAskToChoosePlayersParams: AskToUseActiveSkillParams
---@field targets ServerPlayer[] @ 可以选的目标范围
---@field min_num integer @ 最小值
---@field max_num integer @ 最大值
---@field target_tip_name? string @ 引用的选择目标提示的函数名
---@field choices string[] @ 可选选项列表
---@field all_choices? string[] @ 所有选项
---@field detailed? boolean @ 是否详细显示选项
---@field choice_prompt? string @ 选择提示

--- 询问一名玩家从选项中选择一个，并从targets中选择若干名玩家
---@param room Room @ 房间
---@param player ServerPlayer @ 要做选择的玩家
---@param params HXAskToChoosePlayersParams @ 各种变量
---@return ServerPlayer[], string @ 选择的玩家列表及选项，可能为空
function hxfs.askToChooseChoiceAndPlayers(room, player, params)
  local maxNum, minNum = params.max_num, params.min_num
  if maxNum < 1 then
    return {},""
  end
  params.cancelable = (params.cancelable == nil) and true or params.cancelable
  params.no_indicate = params.no_indicate or false
  params.choices = params.choices or {}
  params.all_choices = params.all_choices or params.choices
  params.detailed = params.detailed or false

  local data = {
    targets = table.map(params.targets, Util.IdMapper),
    num = maxNum,
    min_num = minNum,
    pattern = "",
    skillName = params.skill_name,
    targetTipName = params.target_tip_name,
    extra_data = params.extra_data,
    choices = params.choices,
    all_choices = params.all_choices,
    detailed = params.detailed,
    cancelable = params.cancelable,
    choice_prompt = params.choice_prompt,
  }
  local activeParams = { ---@type AskToUseActiveSkillParams
    skill_name = "#hx_choose_players_skill&",
    prompt = params.prompt or "",
    cancelable = params.cancelable,
    extra_data = data,
    no_indicate = params.no_indicate,
  }
  local choice = room:askToChoice(player, {
    choices = params.choices,
    all_choices = params.all_choices,
    skill_name = params.skill_name,
    cancelable = params.cancelable,
    prompt = params.choice_prompt,
  })
  if not choice then
    choice = ""
  end
  local _, ret = room:askToUseActiveSkill(player, activeParams)

  if ret then
    return ret.targets, choice
  else
    if params.cancelable then
      return {}, ""
    else
      return table.random(params.targets, minNum), choice
    end
  end
end

--- 根据扑克牌点数描述返回数字
---@param str string @ 只能翻译文字为点数
function hxfs.strToNumber(str)
  if str == "A" then
    return 1
  elseif str == "J" then
    return 11
  elseif str == "Q" then
    return 12
  elseif str == "K" then
    return 13
  elseif str == "X" then
    return 0
  end
  return tonumber(str)
end

---@class AskToCustomDialogParamsHX
---@field skill_name string @ 烧条时显示的技能名
---@field qml_path string @ 小游戏框关键词
---@field extra_data any @ 额外信息，因技能而异了

-- Show a qml dialog and return qml's ClientInstance.replyToServer
-- Do anything you like through this function

-- 调用一个自定义对话框，须自备loadData方法，json真是闹麻了
---@param player ServerPlayer @ 询问的角色
---@param params AskToCustomDialogParamsHX @ 各种变量
---@return any @ 返回qml的ClientInstance.replyToServer
function hxfs.askToCustomDialog(player, params)
  local command = "CustomDialog"
  local req = Request:new(player, command)
  req.focus_text = params.skill_name
  req:setData(player, {
    path = params.qml_path,
    data = params.extra_data,
  })
  return req:getResult(player)
end

-- 获取所有限时地主
---@return string[] @ 所有限时地主的武将名
function hxfs.getAllLandlord()
  local landlords = {}
  local extension = {"tenyear_other",}
  local ol_landlords = {
    "wuhushangjiang","ol__caocao","ol__lvbu","nianshou",
    "zhangjiaozhangbaozhangliang","ol__caochong","ol__liru","ol__dengai",
    --"ol__zhugeliang",
  }
  for _, general in pairs(Fk.generals) do
    if table.contains(extension, general.package.name) or table.contains(ol_landlords, general.name) then
      table.insert(landlords, general.name)
    end
  end
  return landlords
end

--- 令玩家获得四象标记
--- @param player ServerPlayer
--- @param mark_type integer
function hxfs.addFourSymbols(player, mark_type)
  local room = player.room
  local mark_name_table = {
    "@!!azure_dragon",
    "@!!white_tiger",
    "@!!vermilion_bird",
    "@!!black_tortoise"
  }
  room:doSuperLightBox("packages/huixiangkill/qml/DivineBeastFly.qml", {playerId = player.id, beastType = mark_type - 1})
  room:delay(1700)
  room:setPlayerMark(player, mark_name_table[mark_type], 1)
end

-- --- MarkChangeData 邪道时机之标记改变
-- ---@class MarkChangeDataSpec
-- ---@field public who ServerPlayer @ 被移除武将的角色
-- ---@field public name string @ 标记名称

-- ---@class hxfs.MarkChangeData: MarkChangeDataSpec, TriggerData
-- hxfs.MarkChangeData = TriggerData:subclass("MarkChangeData")

-- --- 移除武将牌TriggerEvent
-- ---@class hxfs.MarkChangeEvent: TriggerEvent
-- ---@field public data hxfs.MarkChangeData
-- hxfs.MarkChangeEvent = TriggerEvent:subclass("MarkChangeEvent")

-- --- 邪道时机之标记改变时
-- ---@class hxfs.GeneralRemoving: hxfs.MarkChangeEvent
-- hxfs.GeneralRemoving = hxfs.MarkChangeEvent:subclass("hxfs.GeneralRemoving")
-- --- 邪道时机之标记改变后
-- ---@class hxfs.MarkChanged: hxfs.MarkChangeEvent
-- hxfs.MarkChanged = hxfs.MarkChangeEvent:subclass("hxfs.MarkChanged")

-- ---@alias MarkChangeTrigFunc fun(self: TriggerSkill, event: hxfs.MarkChangeEvent,
-- ---  target: ServerPlayer, player: ServerPlayer, data: hxfs.MarkChangeData):any

-- ---@class SkillSkeleton
-- ---@field public addEffect fun(self: SkillSkeleton, key: hxfs.MarkChangeEvent,
-- ---  data: TrigSkelSpec<MarkChangeTrigFunc>, attr: TrigSkelAttribute?): SkillSkeleton

-- --- 移除武将牌GameEvent
-- hxfs.MarkChange = "MarkChange"

-- Fk:addGameEvent(hxfs.MarkChange, nil, function(self)
--   local rgdata = self.data ---@class MarkChangeDataSpec
--   local room = self.room ---@class Room
--   local player, markName = rgdata.who, rgdata.name
--   player:setMark("CompanionEffect", 0)
--   player:setMark("HalfMaxHpLeft", 0)
--   player:doNotify("SetPlayerMark", { player.id, "CompanionEffect", 0})
--   player:doNotify("SetPlayerMark", { player.id, "HalfMaxHpLeft", 0})

--   --if player.kingdom == "unknown" then player:revealGeneral(isDeputy, true) end 
--   player:revealGeneral(isDeputy, true) -- 先摆

--   local orig = isDeputy and (player.deputyGeneral or "") or player.general
--   if orig:startsWith("blank_") then return false end
--   local data = {
--     who = player,
--     isDeputy = isDeputy,
--     origName = orig,
--   }
--   if room.logic:trigger(hxfs.GeneralRemoving, player, data) then return false end

--   orig = isDeputy and (player.deputyGeneral or "") or player.general
--   if orig:startsWith("blank_") then return false end
--   local orig_general = Fk.generals[orig]

--   local orig_skills = orig_general and orig_general:getSkillNameList() or {}

--   local new_general = orig_general.gender == General.Male and "blank_shibing" or "blank_nvshibing"

--   orig_skills = table.map(orig_skills, function(e)
--     return "-" .. e
--   end)

--   room:handleAddLoseSkills(player, table.concat(orig_skills, "|"), nil, false)

--   if isDeputy then
--     room:setPlayerProperty(player, "deputyGeneral", new_general)
--     room:setPlayerMark(player, "__heg_deputy", new_general)
--   else
--     room:setPlayerProperty(player, "general", new_general)
--     room:setPlayerMark(player, "__heg_general", new_general)
--   end

--   player:filterHandcards()
--   room:sendLog{
--     type = "#MarkChanged",
--     from = player.id,
--     arg = isDeputy and "deputyGeneral" or "mainGeneral",
--     arg2 = orig,
--   }
--   room:returnToGeneralPile({orig})

--   room.logic:trigger(hxfs.MarkChanged, player, data)
-- end)


return hxfs